﻿// ReSharper disable CommentTypo
#region Copyright © 2013 Miracle Business Solutions
// All rights are reserved. Reproduction or transmission in whole or in part,
// any form or by any means, electronic, mechanical or otherwise, is prohibited
// without the prior written consent of the copyright owner.

// Document:	Minify\Minify.cs
// Creation Date:	11/11/2013
// Last Edit:		11/11/2013
// Author:		Moreno Sint Hill
#endregion

using System;
using System.Windows.Media;
using JetBrains.Annotations;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Plugins;
using Styx.TreeSharp;


namespace Minify
{
    /// <summary>
    /// A Miracle Business Solutions production to counter the Honorbuddy lag
    /// Special credits to ChinaJade & Millz for providing helpfull information
    /// </summary>
    [UsedImplicitly]
    internal class Minify : HBPlugin
    {
        private bool _lazyLoaded;
        #region Overrides of HBPlugin

        /// <summary>
        /// Called everytime the engine pulses.
        /// </summary>
        public override void Pulse()
        {
            // Install decorated Combat Routine
            // NB: Honorbuddy *loves* to initialize Plugins,
            // Quest Behaviors, etc multiple times.
            // So, we must guard against decorating
            // our decorator.
            if (!_lazyLoaded  && (RoutineManager.Current != null) && (RoutineManager.Current != _decoratedCombatRoutine))
            {
                Logging.Write(Colors.DarkCyan, "[Minify(init)] Re-decorating the Combat Routine with the minified version");
                _undecoratedCombatRoutine = RoutineManager.Current;
                _decoratedCombatRoutine = new MinifiedCR(RoutineManager.Current);
                RoutineManager.Current = _decoratedCombatRoutine;
                _lazyLoaded = true;
                RoutineManager.Reloaded += RoutineManagerOnReloaded;
            }
        }

        private void RoutineManagerOnReloaded(object sender, EventArgs eventArgs)
        {   
            //Save old again
            _undecoratedCombatRoutine = RoutineManager.Current;
            // Install decorated Combat Routine
            // NB: Honorbuddy *loves* to initialize Plugins,
            // Quest Behaviors, etc multiple times.
            // So, we must guard against decorating
            // our decorator.
            if ((RoutineManager.Current != null) && (RoutineManager.Current != _decoratedCombatRoutine))
            {
                Logging.Write(Colors.DarkCyan, "[Minify(reload)] Re-decorating the Combat Routine with the minified version");

                _decoratedCombatRoutine = new MinifiedCR(RoutineManager.Current);
                RoutineManager.Current = _decoratedCombatRoutine;
            }
        }

        /// <summary>
        /// The name of this plugin.
        /// </summary>
        public override string Name
        {
            get { return @"Minify - Lag minimization"; }
        }

        /// <summary>
        /// The author of this plugin.
        /// </summary>
        public override string Author
        {
            get { return @"Mirabis"; }
        }

        /// <summary>
        /// The version of the plugin.
        /// </summary>
        public override Version Version
        {
            get { return new Version(1, 0, 0, 1); }
        }


        /// <summary>
        /// Initialize() equals Enabled
        /// ---
        /// called when user clicks the enable checkbox for the plugin, -or when
        /// HonorBuddy loads plugins initially if already initialized
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();    

            BotEvents.OnBotStarted += BotEventsOnOnBotStarted;
            BotEvents.OnBotStopped += BotEventsOnOnBotStopped;
            //Get Undecorated one
            _lazyLoaded = false;


        }

        private void BotEventsOnOnBotStopped(EventArgs args)
        {
            Logging.Write(Colors.DarkCyan, "[Minify(Stopped)] Bot Stopped - Disabled Minify");
            RoutineManager.Current = _undecoratedCombatRoutine;
        }

        private void BotEventsOnOnBotStarted(EventArgs args)
        {
            Logging.Write(Colors.DarkCyan, "[Minify(Started)] Bot Started - Enabling Minify");
            if (!_lazyLoaded && (RoutineManager.Current != null) && (RoutineManager.Current != _decoratedCombatRoutine))
            {
                Logging.Write(Colors.DarkCyan, "[Minify(init)] Re-decorating the Combat Routine with the minified version");
                _undecoratedCombatRoutine = RoutineManager.Current;
                _decoratedCombatRoutine = new MinifiedCR(RoutineManager.Current);
                RoutineManager.Current = _decoratedCombatRoutine;
                _lazyLoaded = true;
                RoutineManager.Reloaded += RoutineManagerOnReloaded;
            }
        }

        private ICombatRoutine _undecoratedCombatRoutine;
        private ICombatRoutine _decoratedCombatRoutine;

        /// <summary>
        /// Dispose() equals Disabled
        /// ---
        /// called when user unchecks your plugin -or- when HonorBuddy is closing.  Any 
        /// resource, hook, or event you implemented in Initialize() must be released here 
        /// to avoid the side-effects of your plugin continuing to run after the user disables
        /// </summary>
        public override void Dispose()
        {
            Logging.Write(Colors.DarkCyan, "[Minify(dispose)] Resetting Combat Routine to defaults");
            RoutineManager.Reloaded -= RoutineManagerOnReloaded; 
            RoutineManager.Current = _undecoratedCombatRoutine;
            _decoratedCombatRoutine = null;
            _undecoratedCombatRoutine = null;

            BotEvents.OnBotStarted -= BotEventsOnOnBotStarted;
            BotEvents.OnBotStopped -= BotEventsOnOnBotStopped;
            base.Dispose();
        }

      

        #endregion

        #region Overrides of HBCombat Routine
        /// <summary>
        /// Minification (also minimisation or minimization), in computer programming languages
        /// is the process of removing all unnecessary characters from source code without changing its functionality.
        /// </summary>
        public class MinifiedCR : ICombatRoutine
        {
            #region Decorator(s) : Minimization

            /// <summary>
            /// Changes the Ticks, Releases the frame then returns undecorated Combat Behavior
            /// </summary>
            /// <returns>decorated Combat Behavior</returns>
            private Composite MinifiedCombatBehavior()
            {
                //Update TPS
                TreeRoot.TicksPerSecond = 60;
                //Release Frame
                StyxWoW.Memory.ReleaseFrame(true);
                //Do Stuff
                return new FrameLockSelector(_undecoratedCr.CombatBehavior);
            }

            /// <summary>
            /// Changes the Ticks, Releases the frame then returns undecorated Heal Behavior
            /// </summary>
            /// <returns>decorated Heal Behavior</returns>
            private Composite MinifiedHealBehavior()
            {
                //Update TPS
                TreeRoot.TicksPerSecond = 40;
                //Release Frame
                StyxWoW.Memory.ReleaseFrame(true);
                //Do Stuff
                return new FrameLockSelector(_undecoratedCr.HealBehavior);
            }

            /// <summary>
            /// Changes the Ticks, Releases the frame then returns undecorated Rest Behavior
            /// </summary>
            /// <returns>decorated Rest Behavior</returns>
            private Composite MinifiedRestBehavior()
            {
                //Update TPS
                TreeRoot.TicksPerSecond = 15;
                //Release Frame
                StyxWoW.Memory.ReleaseFrame(true);
                //Do Stuff
                return _undecoratedCr.RestBehavior;
            }

            /// <summary>
            /// Changes the Ticks, Releases the frame then returns undecorated Rest Behavior
            /// </summary>
            /// <returns>decorated Rest Behavior</returns>
            private void MinifiedRest()
            {
                //Update TPS
                TreeRoot.TicksPerSecond = 15;
                //Release Frame
                StyxWoW.Memory.ReleaseFrame(true);
                //Do Stuff
                _undecoratedCr.Rest();
            }

            /// <summary>
            /// Changes the Ticks, Releases the frame then returns undecorated Combat Behavior
            /// </summary>
            /// <returns>decorated Combat Behavior</returns>
            private void MinifiedCombat()
            {
                //Update TPS
                TreeRoot.TicksPerSecond = 60;
                //Release Frame
                StyxWoW.Memory.ReleaseFrame(true);
                //Do Stuff
                using (StyxWoW.Memory.AcquireFrame())
                {
                    _undecoratedCr.Combat();
                }
            }

            /// <summary>
            /// Changes the Ticks, Releases the frame then returns undecorated Heal Behavior
            /// </summary>
            /// <returns>decorated Heal Behavior</returns>
            private void MinifiedHeal()
            {
                //Update TPS
                TreeRoot.TicksPerSecond = 40;
                //Release Frame
                StyxWoW.Memory.ReleaseFrame(true);
                //Do Stuff
                using (StyxWoW.Memory.AcquireFrame())
                {
                    _undecoratedCr.Heal();
                }
            }

            #endregion

            /// <summary>
            /// The name of this CombatRoutine
            /// </summary>
            /// <value>
            /// The name.
            /// </value>
            public string Name
            {
                get { return _undecoratedCr.Name; }
            }

            public string ButtonText
            {
                get { return _undecoratedCr.ButtonText; }
            }

            /// <summary>
            /// Whether this CC want the button on the form to be enabled.
            /// </summary>
            /// <value>
            /// <c>true</c> if configuration button is wanted, <c>false</c> otherwise.
            /// </value>
            public bool WantButton
            {
                get { return _undecoratedCr.WantButton; }
            }

            /// <summary>
            /// Called when this CC is selected as the current CC.
            /// </summary>
            public void Initialize()
            {
                _undecoratedCr.Initialize();
            }

            /// <summary>
            /// Called when the button for this CC is pressed.
            /// </summary>
            public void OnButtonPress()
            {
                _undecoratedCr.OnButtonPress();
            }

            /// <summary>
            /// The <see cref="T:Styx.WoWClass"/> to be used with this routine
            /// </summary>
            /// <value>
            /// The class.
            /// </value>
            public WoWClass Class
            {
                get { return StyxWoW.Me.Class; }
            }

            /// <summary>
            /// Pulldistance.
            /// </summary>
            /// <value>
            /// The pulldistance.
            /// </value>
            public double? PullDistance
            {
                get { return CharacterSettings.Instance.PullDistance; }
            }

            /// <summary>
            /// Behavior used in combat
            /// </summary>
            public Composite CombatBehavior
            {
                get { return MinifiedCombatBehavior(); }
            }

            /// <summary>
            /// Behavior used for combat buffs. eg; 'Horn of Winter', 'Power Infusion' etc..
            /// </summary>
            public Composite CombatBuffBehavior
            {
                get { return _undecoratedCr.CombatBuffBehavior; }
            }

            /// <summary>
            /// Custom handling for when you are dead.
            /// </summary>
            public Composite DeathBehavior
            {
                get { return _undecoratedCr.DeathBehavior; }
            }

            /// <summary>
            /// Behavior used when healing
            /// </summary>
            public Composite HealBehavior
            {
                get { return MinifiedHealBehavior(); }
            }

            /// <summary>
            /// Behavior used for moving to targets
            /// </summary>
            public Composite MoveToTargetBehavior
            {
                get { return _undecoratedCr.MoveToTargetBehavior; }
            }

            /// <summary>
            /// Behavior used for buffing, regular buffs like 'Power Word: Fortitude', 'MotW' etc..
            /// </summary>
            public Composite PreCombatBuffBehavior
            {
                get { return _undecoratedCr.PreCombatBuffBehavior; }
            }

            /// <summary>
            /// Behavior used when buffing prior to pulling
            /// </summary>
            public Composite PullBuffBehavior
            {
                get { return _undecoratedCr.PullBuffBehavior; }
            }

            /// <summary>
            /// Behavior used when resting
            /// </summary>
            public Composite RestBehavior
            {
                get { return MinifiedRestBehavior(); }
            }

            /// <summary>
            /// Behavior used when engaging mobs in combat
            /// </summary>
            public Composite PullBehavior
            {
                get { return _undecoratedCr.PullBehavior; }
            }

            /// <summary>
            /// Whether or not to handle being dead. For casting special abilities (Soulstone, Ankh, etc)
            /// </summary>
            public bool NeedDeath
            {
                get { return _undecoratedCr.NeedDeath; }
            }

            /// <summary>
            /// Property indicating if you to heal.
            /// </summary>
            /// <value>
            /// <c>true</c> if heal is needed, <c>false</c> otherwise.
            /// </value>
            public bool NeedHeal
            {
                get { return _undecoratedCr.NeedHeal; }
            }

            /// <summary>
            /// Property indicating if you need combat buffs.
            /// </summary>
            /// <value>
            /// <c>true</c> if combat buffs is needed, <c>false</c> otherwise.
            /// </value>
            public bool NeedCombatBuffs
            {
                get { return _undecoratedCr.NeedCombatBuffs; }
            }

            /// <summary>
            /// Property indicating if you need to rest.
            /// </summary>
            /// <value>
            /// <c>true</c> if rest is needed, <c>false</c> otherwise.
            /// </value>
            public bool NeedRest
            {
                get { return _undecoratedCr.NeedRest; }
            }

            /// <summary>
            /// Property indicating if you need Pull Buffs
            /// </summary>
            /// <value>
            /// <c>true</c> if pull buffs is needed, <c>false</c> otherwise.
            /// </value>
            public bool NeedPullBuffs
            {
                get { return _undecoratedCr.NeedPullBuffs; }
            }

            /// <summary>
            /// Property indicating if you need PreCombatBuffs
            /// </summary>
            /// <value>
            /// <c>true</c> if rest is needed, <c>false</c> otherwise.
            /// </value>
            public bool NeedPreCombatBuffs
            {
                get { return _undecoratedCr.NeedPreCombatBuffs; }
            }

            /// <summary>
            /// Combat actions.
            /// </summary>
            public void Combat()
            {
                MinifiedCombat();
            }

            /// <summary>
            /// Death actions.
            /// </summary>
            public void Death()
            {
                _undecoratedCr.Death();
            }

            /// <summary>
            /// Heal actions
            /// </summary>
            public void Heal()
            {
                MinifiedHeal();
            }

            /// <summary>
            /// Combat buff actions.
            /// </summary>
            public void CombatBuff()
            {
                _undecoratedCr.CombatBuff();
            }

            /// <summary>
            /// PreCombatBuff actions
            /// </summary>
            public void PreCombatBuff()
            {
                _undecoratedCr.PreCombatBuff();
            }

            /// <summary>
            /// Rest actions
            /// </summary>
            public void Rest()
            {
                MinifiedRest();
            }

            /// <summary>
            /// Called in every pulse of the bot. This way you can maintain stuff per-pulse like a plugin.
            /// </summary>
            public void Pulse()
            {
                _undecoratedCr.Pulse();
            }

            /// <summary>
            /// Called when the CC is being disposed.
            /// </summary>
            public void ShutDown()
            {
                _undecoratedCr.ShutDown();
            }

            /// <summary>
            /// Pull Buff actions.
            /// </summary>
            public void PullBuff()
            {
                _undecoratedCr.PullBuff();
            }

            /// <summary>
            /// Pull actions
            /// </summary>
            public void Pull()
            {
                _undecoratedCr.Pull();
            }

            private readonly ICombatRoutine _undecoratedCr;

            public MinifiedCR(ICombatRoutine undecoratedCr)

            {
                if (undecoratedCr == null)
                {
                    throw new Exception("undecoratedCR cannot be null");
                }

                _undecoratedCr = undecoratedCr;
            }

            #region Implementation of IDisposable

            /// <summary>
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// </summary>
            public void Dispose()
            {
                _undecoratedCr.Dispose();
            }

            #endregion
        }

        #endregion

        #region Overrides of PrioritySelector
        // 
        // This behavior wraps the child behaviors in a 'frame lock' which can provide
        // a big performance improvement if the child behaviors makes multiple HB API
        // calls that internally run off a frame in WoW in one CC pulse.
        // 
        private class FrameLockSelector : PrioritySelector
        {
            public FrameLockSelector(params Composite[] children)
                : base(children)
            {
                /*empty*/
            }


            public FrameLockSelector(ContextChangeHandler contextChange, params Composite[] children)
                : base(contextChange, children)
            {
                /*empty*/
            }


            public override RunStatus Tick(object context)
            {
                using (StyxWoW.Memory.AcquireFrame())
                {
                    return base.Tick(context);
                }
            }
        }
        #endregion
    }
}
// ReSharper restore CommentTypo